/**
 * 响应体
 * 仅支持 ipv4
 */

#include "include/core.h"

static void soft_desc(int);
static int req_handle(int);  // 请求处理
static char * explain_url(char *, char *);

static int localsock = 0;

/**
 * 接口函数
 */
void response(void)
{
    int public_swap, swap_size, remoteSocket;
    pid_t pid;

    desc_listener(soft_desc);  // 监听终止信号
    localsock = socket(PF_INET, SOCK_STREAM, 0);
    error_check ((localsock == -1), "建立服务端套接字错误");

    // 服务端地址 sa4
    struct sockaddr_in sa4;
    sa4.sin_family = AF_INET;  // ipv4
    sa4.sin_port = htons(LISTEN_PORT);  // 端口号
    sa4.sin_addr.s_addr = INADDR_ANY;  // 绑定默认网卡

    public_swap = bind(localsock, (struct sockaddr *) &sa4, sizeof(sa4));
    error_check((public_swap == -1), "绑定服务端地址错误");
    public_swap = listen(localsock, MAX_WORKER);
    error_check((public_swap == -1), "监听时错误");

    while (1) {
        struct sockaddr_in sin4;  // ipv4 地址
        swap_size = sizeof(struct sockaddr_in);
        remoteSocket = accept(localsock, (struct sockaddr *) &sin4, &swap_size);
        if (remoteSocket < 0) break;  // 连接失败的情况
        puts("new message");
        pid = fork(); error_check((pid < 0), "创建进程失败");
        if (pid == 0) {
            // 子进程对请求进行处理
            if (req_handle(remoteSocket)) {
                printf("进程 %d 应答失败\n", pid);
                break;
            }
        }
    }
}

/**
 * 请求处理
 * pid：负责应答的子进程 id
 */
static int req_handle(int remoteSocket)
{
    char buffer[REMOTE_BUF_SIZE] = {0};
    int filehd, pubswap, reqsize;
    char urlstr[512];
    char url[512] = TEMP_DIR;  // 静态目录
    char mimetype[40];  // 文件的 mime 类型
    char reqheader[70];

    reqsize = recv(remoteSocket, buffer, sizeof(buffer), 0);
    if (reqsize < 0) return -1;  // 接收失败的情况
    explain_url(buffer, urlstr);  // 取出url
    strcat(url, urlstr);  // 拼接成本地路径
    if (iscgi(url)) {
        // return cgi_link(url, buffer, remoteSocket);
        int swap, api[2], ipa[2];  // 两个管道
        // char reqheader[170];
        pid_t pid;
        if (pipe(api) < 0 || pipe(ipa) < 0) {
            puts("创建管道失败\n");
            exit(0);
        }
        if ((pid = fork()) < 0) {
            puts("创建进程失败\n");
            exit(0);
        } else if (pid > 0) {
            close(api[0]);
            close(ipa[1]);
            sprintf(reqheader, "%s text/html; charset=utf-8\r\n", HEAD_200);
            strcpy(buffer, reqheader);
            send(remoteSocket, buffer, 442, 0);
            memset(buffer, 0, sizeof(buffer));
            if ((swap = read(ipa[0], buffer, REMOTE_BUF_SIZE)) < 0) {
                puts("检测到条件不符\n");
                exit(1);
            }
            send(remoteSocket, buffer, swap, 0);
            shutdown(remoteSocket, 2);
            close(filehd);
        } else {
            close(api[1]);
            close(ipa[0]);
            if (api[0] != STDIN_FILENO) {
                mcc(dup2(api[0], STDIN_FILENO), STDIN_FILENO);
                close(api[0]);
            }
            if (api[0] != STDOUT_FILENO) {
                mcc(dup2(ipa[1], STDOUT_FILENO), STDOUT_FILENO);
                close(ipa[1]);
            }
            execl(url, "apue", (char *)0);
        }
        return 0;
    }
    if ((filehd = open(url, O_RDONLY)) < 0) {  // 取文件类型
        // 无法打开页面，404 not found
        filehd = open(REQ_FILE_404, O_RDONLY);
        sprintf(reqheader, "%s\r\n", HEAD_HTML_404);
    } else {
        file_mime(url, mimetype);
        sprintf(reqheader, "%s%s\r\n", HEAD_200, mimetype);
    }
    strcpy(buffer, reqheader);
    send(remoteSocket, buffer, 426, 0);
    // 读取并得到真实大小
    if ((pubswap = read(filehd, buffer, REMOTE_BUF_SIZE)) < 0) {
        puts("读取文件失败");
        shutdown(remoteSocket, 2);
        exit(0);
    }
    send(remoteSocket, buffer, pubswap, 0);
    shutdown(remoteSocket, 2);
    close(filehd);
    return 0;
}

static char * explain_url(char * buffer, char * c)
{
    strcpy(c, strtok(buffer, " "));
    if (c != NULL) {
        if ((strstr(c, "GET") == NULL) && (strstr(c, "POST") == NULL)) {
            exit(0);
        }
    }
    strcpy(c, strtok(NULL," "));
    return c;
}

// 终止信号调用
static void soft_desc(int sigo)
{
    if (localsock != 0) {
        shutdown(localsock, 2);
    }
    // raise(SIGKILL);
    exit(0);
}